package com.h2cloud.Proxy._3_JdkProxy;

import lombok.Data;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Package: com.h2cloud.Proxy.jdk
 * @ClassName: Client
 * @Author: h2cloud
 * @CreateTime: 2022/5/26 9:27 下午
 * @Description:
 */

// 对生产厂家要求的接口
interface IProducer {
	// 销售
	public void saleProduct(float money);

	// 售后
	public void afterService(float money);
}

// 一个生产者
@Data
class Producer implements IProducer {
	String name = "一个生产者";
	// 销售
	@Override
	public void saleProduct(float money) {
		System.out.println("销售产品，并拿到钱：" + money);
	}

	// 售后
	@Override
	public void afterService(float money) {
		this.saleProduct(money);
		System.out.println("提供售后服务，并拿到钱：" + money);
	}
}

public class Client {
	public static void main(String[] args) {
		final Producer producer = new Producer();
		System.out.println("producer.getName() = " + producer.getName());
		/**
		 *  基于接口的动态代理：
		 *      涉及的类：Proxy
		 *      提供者：JDK官方
		 *  如何创建代理对象：
		 *      使用 Proxy类中的 newProxyInstance方法
		 *  创建代理对象的要求：
		 *      被代理类最少实现一个接口，如果没有则不能使用
		 *  newProxyInstance方法的参数：
		 *      ClassLoader：类加载器
		 *      用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
		 *      Class[]：字节码数组
		 *          让代理对象和被代理对象有相同方法。固定写法。
		 *      InvocationHandler：用于提供增强的代码
		 */
		IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(
				producer.getClass().getClassLoader(),
				producer.getClass().getInterfaces(),
				new InvocationHandler() {
					/**
					 * 作用：执行被代理对象的任何接口方法都会经过该方法
					 * 方法参数的含义
					 * @param proxy   代理对象的引用
					 * @param method  当前执行的方法
					 * @param args    当前执行方法所需的参数
					 * @return 和被代理对象方法有相同的返回值
					 * @throws Throwable
					 */
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						//提供增强的代码
						Object returnValue = null;

						//1.获取方法执行的参数
						Float money = (Float) args[0];
						//2.判断当前方法是不是销售
						if ("saleProduct".equals(method.getName())) {
							System.out.println("proxy saleProduct");
							returnValue = method.invoke(producer, money * 0.8f);
						} else if ("afterService".equals(method.getName())) {
							System.out.println("proxy afterService");
							returnValue = method.invoke(producer, money * 0.8f);
						}
						return returnValue;
					}
				});
		proxyProducer.afterService(10000f);

	}
}
